// ----------------------------------------------------------------------------
// diabindLineNumber.h
// DIA Line number os source file
// ----------------------------------------------------------------------------

#pragma once

// ----------------------------------------------------------------------------

#include "diabindSourceFile.h"

// ----------------------------------------------------------------------------

namespace diabind
{

// ----------------------------------------------------------------------------

interface ILineNumber
{
    virtual ~ILineNumber() {}

    virtual CSymbolPtr get_compiland() = 0;
    virtual CSourceFilePtr get_sourceFile() = 0;
    virtual DWORD get_lineNumber() = 0;
    virtual DWORD get_lineNumberEnd() = 0;
    virtual DWORD get_columnNumber() = 0;
    virtual DWORD get_columnNumberEnd() = 0;
    virtual DWORD get_addressSection() = 0;
    virtual DWORD get_addressOffset() = 0;
    virtual DWORD get_relativeVirtualAddress() = 0;
    virtual ULONGLONG get_virtualAddress() = 0;
    virtual DWORD get_length() = 0;
    virtual bool get_statement() = 0;
};
typedef boost::shared_ptr< ILineNumber > CLineNumberPtr;

// ----------------------------------------------------------------------------

template< class DiaLineNumberT >
class CLineNumberImpl : public ILineNumber
{
    typedef _com_IIID< DiaLineNumberT, &__uuidof(DiaLineNumberT) > CIiid;
    typedef _com_ptr_t< CIiid > CImplPtr;

public:
    explicit CLineNumberImpl(const CImplPtr &pLineNumber)
        : m_pLineNumber(pLineNumber)
    {
    }

    virtual CSymbolPtr get_compiland() override
    {
        return 
            GetCompiland(
                &DiaLineNumberT::get_compiland,
                "IDiaLineNumber::get_compiland");
    }
    virtual CSourceFilePtr get_sourceFile() override
    {
        return 
            GetSourceFile(
                &DiaLineNumberT::get_sourceFile,
                "IDiaLineNumber::get_sourceFile");
    }
#define _diabind_LINENUM_IMPL_PARAM2RET(type, name)                                               \
    virtual type name() override                                                                \
    {                                                                                           \
        return CallMethodOutParamToRet(&DiaLineNumberT::##name, "IDiaLineNumber::" #name);      \
    }
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_lineNumber)
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_lineNumberEnd)
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_columnNumber)
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_columnNumberEnd)
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_addressSection)
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_addressOffset)
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_relativeVirtualAddress)
    _diabind_LINENUM_IMPL_PARAM2RET(ULONGLONG, get_virtualAddress)
    _diabind_LINENUM_IMPL_PARAM2RET(DWORD, get_length)
#define _diabind_LINENUM_IMPL_BOOL(name)                                                          \
    virtual bool name() override                                                                \
    {                                                                                           \
        return !!CallMethodOutParamToRet(&DiaLineNumberT::##name, "IDiaLineNumber::" #name);    \
    }
    _diabind_LINENUM_IMPL_BOOL(get_statement)

protected:
    template< class DiaSymbolT >
    CSymbolPtr GetCompiland(
        HRESULT (STDMETHODCALLTYPE DiaLineNumberT::*pMethod)(DiaSymbolT **),
        const char *szMethodName
    )
    {
        _com_ptr_t< _com_IIID<DiaSymbolT, &__uuidof(DiaSymbolT)> > pSymbol;
        HRESULT hResult = (m_pLineNumber->*pMethod)(&pSymbol);
        if (S_OK != hResult)
            throw CException(hResult, szMethodName);

        return CSymbolPtr( new CSymbolImpl<DiaSymbolT>(pSymbol) );
    }

    template< class DiaSourceFileT >
    CSourceFilePtr GetSourceFile(
        HRESULT (STDMETHODCALLTYPE DiaLineNumberT::*pMethod)(DiaSourceFileT **),
        const char *szMethodName
    )
    {
        _com_ptr_t< _com_IIID<DiaSourceFileT, &__uuidof(DiaSourceFileT)> > pSourceFile;
        HRESULT hResult = (m_pLineNumber->*pMethod)(&pSourceFile);
        if (S_OK != hResult)
            throw CException(hResult, szMethodName);

        return CSourceFilePtr( new CSourceFileImpl<DiaSourceFileT>(pSourceFile) );
    }

    template< typename ArgT >
    ArgT CallMethodOutParamToRet(
        HRESULT (STDMETHODCALLTYPE DiaLineNumberT::*pMethod)(ArgT *),
        const char *szMethodName
    )
    {
        ArgT RetVal;
        HRESULT hResult = (m_pLineNumber->*pMethod)(&RetVal);
        if (S_OK != hResult)
            throw CException(hResult, szMethodName);
        return RetVal;
    }

private:
    CImplPtr m_pLineNumber;
};

// ----------------------------------------------------------------------------

}   // namespace diabind

// ----------------------------------------------------------------------------

